import fs from 'node:fs';
import path from 'node:path';
import {
    merge,
    execCommand,
    mergeOrCreateFile,
    getPackageMangerName
} from 'mv-common';
import MoveModuleDependencies from '@mtool/move-module-dependencies';
import {
    DEPENDENCIES,
    IGNORE_COVER_FILE,
    DEPENDENCIES_PROXY,
    ESLINT_PRETTIER_PATH,
    HUSKY_AND_LINT_STAGED,
    HUSKY_LINT_STAGED_SCRIPT,
    STANDARDIZATION_COMMAND,
    LINT_STAGED_NAME
} from '@/constance';
import type {
    PackageJSON,
    Dependencies
} from '@mtool/move-module-dependencies';
import { StandardizationProps } from './type';

/**
 * 导出类型
 * **/
export { StandardizationProps, Dependencies, PackageJSON };

class Standardization extends MoveModuleDependencies {
    public static instance: Standardization;
    public static getInstance(): Standardization {
        if (!Standardization.instance)
            Standardization.instance = new Standardization();
        return Standardization.instance;
    }
    public default: StandardizationProps = {
        needDependenciesProxy: false,
        needHuskyAndLintStaged: false,
        dependencies: DEPENDENCIES,
        movePath: [],
        coverIgnore: {
            file: IGNORE_COVER_FILE
        }
    };
    public moduleConfig = this.default;

    /**
     * 判断是否存在git文件
     * **/
    public hasGit() {
        return fs.existsSync(path.resolve(this.rootPath, '.git'));
    }

    /**
     * 开始移动
     * @param { Object } option 执行指令参数
     * **/
    public async start(option: Partial<StandardizationProps>): Promise<void> {
        this.moduleConfig = merge.recursive(this.default, option);
        if (!(await this.verifyRootPath(process.cwd()))) return;

        // 默认注入 eslint 和 prettier 的配置文件
        this.moduleConfig.movePath.push({
            origin: ESLINT_PRETTIER_PATH,
            dest: this.rootPath
        });

        // 当前项目下不存在 git 时，husky 以及 lint-staged 安装无效
        if (this.moduleConfig.needHuskyAndLintStaged && !this.hasGit()) {
            this.moduleConfig.needHuskyAndLintStaged = false;
            console.error(
                'the project not git, ignore husky and lint-staged install...'
            );
        }

        // 是否需要 husky 以及 lint-staged, 并赋值安装配置
        const { needDependenciesProxy, needHuskyAndLintStaged } =
            this.moduleConfig;

        const config = { ...this.moduleConfig };
        delete config.needDependenciesProxy;
        delete config.needHuskyAndLintStaged;

        this.startWorker(config)
            .then(() => {
                //  proxy 代理文件后置给到
                if (needDependenciesProxy) {
                    this.packageCli =
                        getPackageMangerName(this.rootPath) || 'npm';
                    const cliFileName =
                        this.packageCli === 'yarn' ? '.yarnrc' : '.npmrc';
                    mergeOrCreateFile(
                        path.resolve(DEPENDENCIES_PROXY, cliFileName),
                        path.resolve(this.rootPath, cliFileName),
                        {
                            wrap: true
                        }
                    );
                }

                // 如果安装了 husky 以及 lint-staged 则执行约束
                if (needHuskyAndLintStaged) {
                    const standardizationStr = ['npm', 'vanner'].includes(
                        this.packageCli as string
                    )
                        ? `${this.packageCli} run ${STANDARDIZATION_COMMAND}`
                        : `${this.packageCli} ${STANDARDIZATION_COMMAND}`;
                    execCommand(
                        `echo ${standardizationStr} > .husky/pre-commit`,
                        { cwd: this.rootPath, stdio: 'inherit' }
                    );

                    // 在根目录下新建 lint-staged 文件
                    fs.writeFileSync(
                        path.resolve(this.rootPath, LINT_STAGED_NAME),
                        `module.exports = {\n'    {src}/**/*.{js,ts,jsx,tsx}': ['${standardizationStr}']\n};`,
                        { encoding: 'utf-8' }
                    );
                }
            })
            .catch((e) => {
                console.log('standardization exec fail...', e);
            });
    }

    /**
     * 在执行 install 安装之前，先卸载本次需要安装的依赖
     * **/
    public async uninstallDependencies(): Promise<void> {
        const { dependencies } = this.moduleConfig;
        const dependenciesKeys = Object.keys(dependencies.dependencies).concat(
            Object.keys(dependencies.devDependencies)
        );
        const removeStr = ['npm', 'pnpm', 'vanner'].includes(
            this.packageCli as string
        )
            ? 'uninstall'
            : 'remove';

        console.log('ready remove need standardization dependencies...');
        await execCommand(
            `${this.packageCli} ${removeStr} ${dependenciesKeys.join(' ')}`,
            { cwd: this.rootPath, stdio: 'inherit' }
        );
        console.log('remove need standardization success...');
    }

    /**
     * 添加 husky 以及 lint-staged 的配置
     * @param { Object } content 不添加package中的内容
     * **/
    public async useAddPackageJSON(content: PackageJSON): Promise<PackageJSON> {
        await this.uninstallDependencies();
        if (this.moduleConfig.needHuskyAndLintStaged) {
            content.devDependencies = {
                ...content.devDependencies,
                ...HUSKY_AND_LINT_STAGED
            };
            content.scripts = {
                ...content.scripts,
                ...HUSKY_LINT_STAGED_SCRIPT
            };
        }
        return content;
    }
}

export default Standardization.getInstance();
